﻿// ===============================================================================
//  Copyright © ymind.net. All rights reserved .
//  官方网站：http://ymind.net/
//  版权所有：彦铭工作室 老陈出品
// ===============================================================================

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using Lcsoft.TemplateEngine.Core;

namespace Lcsoft.TemplateEngine.XHtmlParser
{
	/// <summary>
	/// 表示 xhtml 文档中的节点的集合。
	/// </summary>
	[ComVisible(true)]
	[Serializable]
	public class XHtmlNodeCollection : List<XHtmlNode>
	{
		private readonly XHtmlElement _parent;

		/// <summary>
		/// 初始化 <see cref="XHtmlNodeCollection"/> 类的新实例。
		/// </summary>
		/// <param name="parent">包含此 <see cref="XHtmlNodeCollection"/> 集合的 <see cref="XHtmlElement"/>。</param>
		public XHtmlNodeCollection(XHtmlElement parent) { this._parent = parent; }

		/// <summary>
		/// 向集合中添加一个 <see cref="XHtmlNode"/>。
		/// </summary>
		/// <param name="node">一个 <see cref="XHtmlNode"/>。</param>
		/// <returns>如果添加成功则返回 <paramref name="node"/> 在集合中的索引。</returns>
		public new int Add(XHtmlNode node)
		{
			if (null == node) throw new ParseException("参数错误", "node 必须包含一个有效的 XHtmlNode 类或其派生类的实例。");
			if (null != this._parent) node.SetParent(this._parent);

			base.Add(node);

			return this.IndexOf(node);
		}

		/// <summary>
		/// 在集合的指定索引处插入一个 <see cref="XHtmlNode"/>。
		/// </summary>
		/// <param name="index">要插入到的位置的从零开始的索引。</param>
		/// <param name="node">一个 <see cref="XHtmlNode"/>。</param>
		public new void Insert(int index, XHtmlNode node)
		{
			if (null == node) throw new ParseException("参数错误", "node 必须包含一个有效的 XHtmlNode 类或其派生类的实例。");
			if (null != this._parent) node.SetParent(this._parent);

			base.Insert(index, node);
		}

		/// <summary>
		/// 在集合中搜索与指定条件相匹配的项并返回其集合。
		/// </summary>
		/// <typeparam name="T">要查找的对象的类型。</typeparam>
		/// <param name="predicate">匹配条件。</param>
		/// <returns>返回 <see cref="List&lt;T&gt;"/>。</returns>
		public List<T> FindNodes<T>(Predicate<T> predicate) where T : class
		{
			var list = new List<T>();

			foreach (var tNode in this.OfType<T>())
			{
				if (predicate == null) list.Add(tNode);
				else if (predicate.Invoke(tNode)) list.Add(tNode);
			}

			return list;
		}

		/// <summary>
		/// 在集合中搜索指定类型的项并返回其集合（含子节点）。
		/// </summary>
		/// <typeparam name="T">要查找的对象的类型。</typeparam>
		/// <returns>返回 <see cref="List&lt;T&gt;"/>。</returns>
		public List<T> FindAllNodes<T>() where T : class { return this.FindAllNodes<T>(null); }

		/// <summary>
		/// 在集合中搜索与指定条件相匹配的指定类型的项并返回其集合（含子节点）。
		/// </summary>
		/// <typeparam name="T">要查找的对象的类型。</typeparam>
		/// <param name="predicate">匹配条件。</param>
		/// <returns>返回 <see cref="List&lt;T&gt;"/>。</returns>
		public List<T> FindAllNodes<T>(Predicate<T> predicate) where T : class
		{
			var list = new List<T>();

			foreach (var node in this)
			{
				var tNode = node as T;

				if (tNode != null)
				{
					if (predicate == null) list.Add(tNode);
					else if (predicate.Invoke(tNode)) list.Add(tNode);
				}

				var element = node as XHtmlElement;

				if (element == null) continue;

				list.AddRange(element.Nodes.FindAllNodes(predicate));
			}

			return list;
		}

		/// <summary>
		/// 从集合中移除与指定条件相匹配的所有项。
		/// </summary>
		/// <typeparam name="T">要移除的对象的类型。</typeparam>
		/// <param name="predicate">匹配条件。</param>
		public void RemoveNodes<T>(Predicate<T> predicate) where T : class
		{
			for (var index = 0; index < this.Count; index++)
			{
				var node = this[index];

				var tNode = node as T;

				if (tNode == null) continue;

				if (predicate == null) this.Remove(node);
				else if (predicate.Invoke(tNode)) this.Remove(node);
			}
		}

		/// <summary>
		/// 从集合中移除指定类型的所有项（含子节点）。
		/// </summary>
		/// <typeparam name="T">要移除的对象的类型。</typeparam>
		public void RemoveAllNodes<T>() where T : class { this.RemoveAllNodes<T>(null); }

		/// <summary>
		/// 从集合中移除与指定条件相匹配的指定类型的所有项（含子节点）。
		/// </summary>
		/// <typeparam name="T">要移除的对象的类型。</typeparam>
		/// <param name="predicate">匹配条件。</param>
		public void RemoveAllNodes<T>(Predicate<T> predicate) where T : class
		{
			for (var index = 0; index < this.Count; index++)
			{
				var node = this[index];

				var tNode = node as T;

				if (tNode != null)
				{
					if (predicate == null) this.Remove(node);
					else if (predicate.Invoke(tNode)) this.Remove(node);
				}

				var element = node as XHtmlElement;

				if (element == null) continue;

				element.Nodes.RemoveAllNodes(predicate);
			}
		}

		#region 公有属性

		/// <summary>
		/// 获取指定索引处的 <see cref="XHtmlNode"/>。
		/// </summary>
		/// <param name="index">要获取的 <see cref="XHtmlNode"/> 从零开始的索引。</param>
		/// <returns>返回 <see cref="XHtmlNode"/>。</returns>
		public new XHtmlNode this[int index]
		{
			get { return base[index]; }
			set
			{
				if (null == value) throw new ParseException("参数错误", "value 必须包含一个有效的 XHtmlNode 类或其派生类的实例。");
				if (null != this._parent) value.SetParent(this._parent);

				base[index] = value;
			}
		}

		/// <summary>
		/// 返回具有指定名称的 <see cref="XHtmlNode"/>。
		/// </summary>
		/// <param name="name"><see cref="XHtmlNode"/> 的名称。</param>
		/// <returns>返回 <see cref="XHtmlNode"/>。</returns>
		public XHtmlElement this[string name]
		{
			get
			{
				var results = this.FindNodes<XHtmlElement>(item => String.Compare(item.Name, name, StringComparison.OrdinalIgnoreCase) == 0);

				return results.Count > 0 ? results[0] : null;
			}
		}

		#endregion
	}
}
